package com.laolei.dubbo.config;

import java.util.List;

import javax.servlet.ServletContextListener;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.bind.PropertiesConfigurationFactory;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.util.SocketUtils;
import org.springframework.util.StringUtils;

import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.MonitorConfig;
import com.alibaba.dubbo.config.ProtocolConfig;
import com.alibaba.dubbo.config.ReferenceConfig;
import com.alibaba.dubbo.config.RegistryConfig;
import com.alibaba.dubbo.config.ServiceConfig;
import com.alibaba.dubbo.config.spring.AnnotationBean;
import com.alibaba.dubbo.remoting.http.servlet.BootstrapListener;

@Configuration
public class DubboxAutoConfiguration extends AnnotationBean implements EnvironmentAware {

	static Logger log = LoggerFactory.getLogger(DubboxAutoConfiguration.class);

	private static final long serialVersionUID = 1L;

	ConfigurableEnvironment environment;

	@Override
	public void setEnvironment(Environment environment) {
		this.environment = (ConfigurableEnvironment) environment;
	}

	public <T> T getPropertiesConfigurationBean(String targetName, Class<T> types) {
		PropertiesConfigurationFactory<T> factory = new PropertiesConfigurationFactory<T>(types);
		factory.setPropertySources(environment.getPropertySources());
		factory.setConversionService(environment.getConversionService());
		factory.setIgnoreInvalidFields(false);
		factory.setIgnoreUnknownFields(true);
		factory.setExceptionIfInvalid(true);
		factory.setIgnoreNestedProperties(false);
		factory.setTargetName(targetName);
		try {
			factory.bindPropertiesToTarget();
			return factory.getObject();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		DubboxConfig dubboxConfig = this.getPropertiesConfigurationBean(DubboxConfig.targetName, DubboxConfig.class);
		this.registerThis(dubboxConfig.getBasePackage(), beanFactory);
		this.registerApplication(dubboxConfig.getApplication(), beanFactory);
		this.registerProtocols(dubboxConfig.getProtocols(), beanFactory);
		this.registerRegistry(dubboxConfig.getRegistry(), beanFactory);
		this.registerMonitor(dubboxConfig.getMonitor(), beanFactory);
		this.registerReferences(dubboxConfig.getReferences(), beanFactory);
		this.registerServices(dubboxConfig.getServices(), beanFactory);
	}

	private void registerReferences(List<ReferenceConfig<?>> references, ConfigurableListableBeanFactory beanFactory) {
		if (references == null || references.isEmpty()) {
			return;
		}
		for (ReferenceConfig<?> referenceConfig : references) {
			String beanName = referenceConfig.getId() + "-ReferenceConfig";
			beanFactory.registerSingleton(beanName, referenceConfig);
			log.debug("注册调用信息{} 完毕", beanName);
		}
	}

	private void registerServices(List<ServiceConfig<?>> services, ConfigurableListableBeanFactory beanFactory) {
		if (services == null || services.isEmpty()) {
			return;
		}
		for (ServiceConfig<?> serviceConfig : services) {
			String beanName = serviceConfig.getId() + "-ServiceConfig";
			beanFactory.registerSingleton(beanName, serviceConfig);
			log.debug("注册服务信息{} 完毕", beanName);
		}
	}


	private void registerMonitor(MonitorConfig monitorConfig, ConfigurableListableBeanFactory beanFactory) {
		if (monitorConfig != null)
			beanFactory.registerSingleton("registryConfig", monitorConfig);
		else
			log.debug("dubbox 没有配置服务监控中心");
	}

	private void registerRegistry(RegistryConfig registryConfig, ConfigurableListableBeanFactory beanFactory) {
		if (registryConfig != null)
			beanFactory.registerSingleton("registryConfig", registryConfig);
		else
			log.info("dubbox 没有配置服务名注册中心");
	}

	private void registerThis(String annotationPackages, ConfigurableListableBeanFactory beanFactory) {
		if (StringUtils.isEmpty(annotationPackages))
			log.warn(" dubbox没有配置注解服务所在的目录");
		super.setPackage(annotationPackages);
		super.setId("dubboxAnnotationPackageS");
		super.postProcessBeanFactory(beanFactory);
	}

	private void registerApplication(ApplicationConfig applicationConfig, ConfigurableListableBeanFactory beanFactory) {
		if (applicationConfig != null)
			beanFactory.registerSingleton(applicationConfig.getName(), applicationConfig);
		else
			log.warn("dubbox 没有配置服务名信息");
	}

	private void registerProtocols(List<ProtocolConfig> protocols, ConfigurableListableBeanFactory beanFactory) {
		if (protocols == null || protocols.isEmpty()) {
			log.debug("dubbox 没有配置协议,将使用默认协议");
			return;
		}
		for (ProtocolConfig protocolConfig : protocols) {
			String beanName = protocolConfig.getName();
			if(protocolConfig.getPort()==null||protocolConfig.getPort()==0)
				protocolConfig.setPort(SocketUtils.findAvailableTcpPort(53600, 53688));
			beanFactory.registerSingleton(beanName, protocolConfig);
			log.debug("注册协议信息{} 完毕", beanName + "-ProtocolConfig");
		}
	}
	@Bean
	@ConditionalOnClass(ServletContextListener.class)
	public ServletListenerRegistrationBean<BootstrapListener> listenerRegistrationBean(){
		BootstrapListener bootstrapListener=new BootstrapListener();
		ServletListenerRegistrationBean<BootstrapListener> listenerRegistrationBean=new ServletListenerRegistrationBean<BootstrapListener>(bootstrapListener);
		listenerRegistrationBean.setName("Dubbox-Bootstrap-Listener");
		return listenerRegistrationBean;
	}

}
